Skip to content
Go back

EasyFill 重大更新,全面提升用户体验

Published:  at  11:27 PM

版本:v1.1.1

经过两个月的偷懒,EasyFill 迎来了 v1.1.1 版本的重大更新。这次更新主要在匹配算法上进行大幅度优化,全面提升匹配效率

更新概览


全新识别方式

1. 动态 Shadow DOM 支持

我发现有些评论系统通过 Shadow DOM 来实现封装,导致 v1.0 版本无法识别 Shadow DOM 生成的表单。在 v1.1.1 中,EasyFill 新增了对动态创建的 Shadow DOM 的完整支持。

function traverseShadowDOM(root: Document | ShadowRoot | Element) {
  const inputs = root.querySelectorAll('input, textarea');
  elements.push(...Array.from(inputs));
  
  const allElements = root.querySelectorAll('*');
  allElements.forEach(element => {
    if (element.shadowRoot) {
      logger.info('发现 Shadow DOM,正在遍历', { 
        tagName: element.tagName, 
        shadowRootMode: element.shadowRoot.mode 
      });
      traverseShadowDOM(element.shadowRoot);
    }
  });
}

2. 三种识别方式全覆盖

在 v1.0 版本,EasyFill 只支持 name 字段识别。为了更加准确的匹配字段,引入了全新三种字段识别方式,确保在各种稀奇古怪的表单都可以识别:

Placeholder 识别

通过分析输入框的 placeholder 属性来识别字段类型:

<input placeholder="请输入您的姓名" />
<input placeholder="邮箱地址" />
<input placeholder="个人网站" />

Type 识别

基于 HTML5 标准的 type 属性进行智能识别:

<input type="email" />
<input type="url" />
<input type="text" name="username" />

ID 识别

通过元素的 id 属性进行精确匹配:

<input id="author" />
<input id="email" />
<input id="website" />

匹配策略:

inputs.forEach((input) => {
  const typeAttr = (input.getAttribute("type") || "").toLowerCase();
  const nameAttr = (input.getAttribute("name") || "").toLowerCase();
  const idAttr = (input.getAttribute("id") || "").toLowerCase();
  let valueToSet = ""; // 要填充的值
  let matchedBy = "";  // 匹配方式(id, name, type)
  let fieldType = "";  // 字段类型(name, email, url)

  // 匹配 URL 字段
  if (keywordSets.url.has(nameAttr) || keywordSets.url.has(`#${idAttr}`)) {
    valueToSet = url;
    matchedBy = keywordSets.url.has(`#${idAttr}`) ? "id" : "name";
    fieldType = "url";
  } else if (typeAttr === "url" && url) {
    valueToSet = url;
    matchedBy = "type";
    fieldType = "url";
  }

  // 匹配 Email 字段
  else if (keywordSets.email.has(nameAttr) || keywordSets.email.has(`#${idAttr}`)) {
    valueToSet = email;
    matchedBy = keywordSets.email.has(`#${idAttr}`) ? "id" : "name";
    fieldType = "email";
  } else if (typeAttr === "email" && email) {
    valueToSet = email;
    matchedBy = "type";
    fieldType = "email";
  }

  // 匹配 Name 字段
  else if ((keywordSets.name.has(nameAttr) || keywordSets.name.has(`#${idAttr}`)) && name) {
    valueToSet = name;
    matchedBy = keywordSets.name.has(`#${idAttr}`) ? "id" : "name";
    fieldType = "name";
  }

  // 没有匹配上就跳过
  if (!valueToSet) return;

  // 设置值并触发事件
  (input as HTMLInputElement).value = valueToSet;
  input.dispatchEvent(new Event('input', { bubbles: true }));
  input.dispatchEvent(new Event('change', { bubbles: true }));

  // 记录日志
  logger.info('填充表单字段', {
    name: nameAttr || "",
    id: idAttr || "",
    type: typeAttr || "",
    matchedBy,
    valueToSet,
    inShadowDOM: isInShadowDOM(input)
  });
});

数据同步

1. 自定义数据源功能

v1.1.1 版本允许用户完全自定义关键字数据源。

该源来自我的腾讯云 COS,且由腾讯云境内 CDN 加速,基本上无延迟:

https://cos.lhasa.icu/EasyFill/keywords.json

自定义数据源格式示例:

{
  "name": ["name", "author", "username", "昵称", "姓名"],
  "email": ["email", "mail", "邮箱", "电子邮件"],
  "url": ["url", "website", "blog", "网站", "博客"]
}

2. 缓存机制

实现了基于 HTTP 标准的智能缓存系统,大幅减少不必要的网络请求:

ETag 和 Last-Modified 支持:

if (etag && !forceSync) {
  headers['If-None-Match'] = etag;
}
if (lastModified && !forceSync) {
  headers['If-Modified-Since'] = lastModified;
}

性能优化

1. localStorage 持久化存储

实现 Markdown 内容的持久化机制:

const fetchMarkdown = async (url: string) => {
  try {
    // 检查 localStorage 是否已有缓存
    const cachedMarkdown = localStorage.getItem(url);
    if (cachedMarkdown) {
      logger.info(`从缓存加载 Markdown 文件: ${url}`);
      return cachedMarkdown;
    }

    // 如果没有缓存,从网络加载
    const response = await fetch(url);
    const markdown = await response.text();

    // 将加载的内容存入 localStorage
    localStorage.setItem(url, markdown);
    return marked(markdown);
  } catch (error) {
    logger.error(`加载 Markdown 文件失败: ${url}`, error);
  }
};

2. 异步并行加载优化

实现 Markdown 内容的异步并行加载:

const loadContent = async () => {
  const [aboutAuthor, recommendedPlugins, updateLog, privacyPolicy] = await Promise.all([
    fetchMarkdown('/markdowns/about-author.md'),
    fetchMarkdown('/markdowns/recommended-plugins.md'),
    fetchMarkdown('/markdowns/UpdateLog.md'),
    fetchMarkdown('/markdowns/privacy-policy.md'),
  ]);

  setAboutAuthorContent(aboutAuthor);
  setRecommendedPluginsContent(recommendedPlugins);
  setUpdateLogContent(updateLog);
  setPrivacyPolicyContent(privacyPolicy);
};

日志系统

1. 三级别日志架构

EasyFill v1.1.1 实现了单例日志系统,支持 INFO、WARN、ERROR 三个级别:

export enum LogLevel {
  INFO = 'INFO',
  WARN = 'WARN',
  ERROR = 'ERROR',
}

2. 智能环境适配

日志系统能够根据运行环境自动调整输出策略:

public configureByEnvironment(): Logger {
  const isProd = typeof process !== 'undefined' && process.env && process.env.NODE_ENV === 'production';
  
  if (isProd) {
    // 生产环境:只显示警告和错误
    this.setLevel(LogLevel.WARN);
  } else {
    // 开发环境:显示所有日志,并启用彩色和时间戳
    this.setLevel(LogLevel.INFO)
        .useColors(true)
        .showTimestamp(true);
  }
  
  return this;
}

3. 控制台命令

生产状态下,日志默认关闭。所以,增加了命令调试:

// 启用日志系统
EasyFillLogger.enable()

// 关闭日志系统
EasyFillLogger.disable()

// 查看当前状态
EasyFillLogger.status()

命令绑定在全局 window 对象上,重启浏览器仍有效。

在浏览器扩展环境中,使用 chrome.storage.local 来存储,在普通网页环境中,使用 localStorage。

一样的是都用 easyfill_logger_enabled 这个键来存储

4. 链式配置接口

支持灵活的链式配置:

logger
  .setLevel(LogLevel.INFO)
  .useColors(true)
  .showTimestamp(true)
  .setPrefix('[EasyFill]')
  .setPrefixColor('color: #4CAF50; font-weight: bold');

配置选项:


隐私权政策

v1.1.1 版本对隐私权政策进行了全面更新:

主要更新内容:


界面改进

1. 同步设置

新增了直观的同步设置界面,轻松管理数据同步:

2. 状态反馈优化


短期计划

长远计划


致谢与支持

EasyFill 的每一次进步都离不开用户的支持和反馈。特别感谢:Mainbranch 的反馈与支持

如果您觉得 EasyFill 对您有帮助,欢迎:

请我喝一杯咖啡

立即体验

EasyFill v1.1.1 现已在 Chrome 应用商店正式发布,您可以:

  1. 新用户:直接在 Chrome 应用商店搜索 “EasyFill” 安装
  2. 现有用户:通过梯子 Chrome 扩展将自动更新到最新版本
  3. 开发者:访问 GitHub 仓库 查看源代码

EasyFill - 简易填充,让每一次评论更自然,与你的博友互动无缝连接


Suggest Changes

Next Post
端午骑行:倍鱼线